package net.oschina.arvin.dbjava.parse;

import net.oschina.arvin.dbjava.model.FieldDescriptor;
import net.oschina.arvin.dbjava.model.ImportDescriptor;
import net.oschina.arvin.dbjava.model.JavaModelDescriptor;
import net.oschina.arvin.dbjava.util.FileUtils;
import net.oschina.arvin.dbjava.util.Utils;

import java.io.File;
import java.util.*;

/**
 * model java文件解析器
 *
 * @author Arvin
 * @time 2017/9/29 17:10
 */
public class ModelParser {

    private JavaModelDescriptor javaModelDescriptor;

    /** java model 文件源码 */
    private final File sourceFile;

    /** 源码 */
    private final List<String> sourceCodeList;

    /** 主键名称集合 */
    private Set<String> primaryKeyNameSet = new HashSet<String>();

    /** 唯一键集合 */
    private Set<String> uniqueNameSet = new HashSet<String>();

    public ModelParser(File sourceFile) {
        this.sourceFile = sourceFile;
        this.sourceCodeList = FileUtils.readAsStringList(sourceFile);
        primaryKeyNameSet.add("id");
    }

    public void addPrimaryKeys(String... primaryKeys) {
        if (null != primaryKeys && primaryKeys.length > 0) {
            for (String pk : primaryKeys) {
                if (Utils.isNotBlank(pk)) {
                    primaryKeyNameSet.add(pk.trim());
                }
            }
        }
    }

    public void addUniqueKeys(String... uniqueKeys) {
        if (null != uniqueKeys && uniqueKeys.length > 0) {
            for (String uk : uniqueKeys) {
                if (Utils.isNotBlank(uk)) {
                    uniqueNameSet.add(uk.trim());
                }
            }
        }
    }

    public Set<String> getUniqueNameSet() {
        return uniqueNameSet;
    }

    public Set<String> getPrimaryKeyNameSet() {
        return primaryKeyNameSet;
    }

    public File getSourceFile() {
        return sourceFile;
    }

    private volatile boolean isParsed = false;

    public JavaModelDescriptor parse() {
        if (isParsed) {
            return javaModelDescriptor;
        }

        JavaModelDescriptor javaModelDescriptor = new JavaModelDescriptor();

        parsePackageName(javaModelDescriptor);

        int commentScanStartIndex = parseImport(javaModelDescriptor);

        int commentParamStartIndex = parseModelComment(commentScanStartIndex, javaModelDescriptor);

        int declareStartIndex = parseModelCommentParamMap(commentParamStartIndex, javaModelDescriptor);

        int fieldStartIndex = parseModelClassName(declareStartIndex, javaModelDescriptor);

        int methodStartIndex = parseModelFields(fieldStartIndex, javaModelDescriptor);

        this.javaModelDescriptor = javaModelDescriptor;
        return javaModelDescriptor;
    }

    /**
     * 解析属性
     * @return 返回方法开始下标
     */
    private int parseModelFields(int fieldStartIndex, JavaModelDescriptor javaModelDescriptor) {
        int size = sourceCodeList.size();
        int index = fieldStartIndex;

        StringBuilder preCommentLineBuilder = new StringBuilder("");
        String preFieldLine = null;

        List<FieldDescriptor> fieldDescriptorList = new ArrayList<FieldDescriptor>();

        for (; index < size; ++index) {
            String line = sourceCodeList.get(index).trim();

            // 下面正则表示一个get/set方法
            if (!line.matches("(?i)[^\\s]+\\s+[^\\s]+\\s+(get|set|is)[^\\s]+\\s*\\(.*\\)\\s*\\{\\s*$")) {
                if (Utils.isNotBlank(line)) {
                    if (line.startsWith("/") || line.startsWith("*")) { // 注释行

                    } else { // 代码行

                    }
                }
            } else {
                break;
            }
        }

        javaModelDescriptor.setFieldList(fieldDescriptorList);

        return index;
    }

    /**
     * 解析模型类的类名
     *
     * @return 返回属性开始位置
     */
    private int parseModelClassName(int declareStartIndex, JavaModelDescriptor javaModelDescriptor) {
        int size = sourceCodeList.size();
        int index = declareStartIndex;

        for (; index < size; ++index) {
            String line = sourceCodeList.get(index).trim();
            if (line.startsWith("public")) {
                javaModelDescriptor.setClassName(line.replaceAll("^.*\\s*class\\s*([^\\s]+)\\s*\\{$", "$1").trim());
                break;
            }
        }
        return index;
    }

    /**
     * 解析类注释参数
     *
     * @return 返回类定义开始行
     */
    private int parseModelCommentParamMap(int commentParamStartIndex, JavaModelDescriptor javaModelDescriptor) {

        int size = sourceCodeList.size();
        int index = commentParamStartIndex;
        Map<String, String> commentParamsMap = new HashMap<String, String>();

        String paramName = null;
        StringBuilder paramValueBuilder = new StringBuilder();

        for (; index < size; ++index) {
            String line = sourceCodeList.get(index).trim();

            if (Utils.isNotBlank(line)) {
                if (!line.startsWith("public")) { // 参数行
                    if (line.matches("^\\*\\s*@.*$")) { // 参数定义
                        String tempParamName = line.replaceAll("^[^@]*\\s@([^\\s]+).*$", "$1").trim();
                        String subParamValue = line.replaceAll("^[^@]*\\s@[^\\s]+(.*)$", "$1").trim();
                        if (paramName != null && !tempParamName.equals(paramName)) {
                            // 将上一个进行存储
                            commentParamsMap.put(paramName, paramValueBuilder.toString());
                            // 重置
                            paramValueBuilder.setLength(0);
                        }
                        paramName = tempParamName;
                        paramValueBuilder.append(subParamValue).append(";");
                    } else {
                        if (!line.matches(".*\\*/\\s*$")) {
                            String subParamValue = line.replaceAll("(?i)\\s*\\*\\s*(.*)$", "$1").trim();
                            paramValueBuilder.append(subParamValue).append(";");
                        }
                    }
                } else {
                    break;
                }
            }

        }
        // 将最后一个存储
        if (Utils.isNotBlank(paramName)) {
            commentParamsMap.put(paramName, paramValueBuilder.toString());
        }

        javaModelDescriptor.setCommentParamsMap(commentParamsMap);
        return index;
    }

    private int parseModelComment(int commentScanStartIndex, JavaModelDescriptor javaModelDescriptor) {

        int size = sourceCodeList.size();
        int index = commentScanStartIndex;
        StringBuilder commentBuilder = new StringBuilder("");
        for (; index < size; ++index) {
            String line = sourceCodeList.get(index).trim();
            if (!line.startsWith("public") && !line.matches("^\\*\\s*@.*$")) {
                if (!line.matches("^\\*\\s*</?pre>[\\s]*$") && !line.startsWith("/")) {
                    commentBuilder.append(line.replaceAll("^\\*\\s*", "")).append(";");
                }
            } else {
                break;
            }
        }
        javaModelDescriptor.setComment(commentBuilder.toString().trim().replaceAll("^;*", "").replaceAll(";*$", ""));
        return index;
    }

    /**
     * 解析Import
     *
     * @return 返回最后一个Import的下一行起始小标
     */
    private int parseImport(JavaModelDescriptor javaModelDescriptor) {

        int size = sourceCodeList.size();
        List<ImportDescriptor> importDescriptorList = new ArrayList<ImportDescriptor>();
        int index;
        int lastImportIndex = 0;
        for (index = 0; index < size; ++index) {
            String line = sourceCodeList.get(index).trim();
            if (line.startsWith("import")) {
                importDescriptorList.add(new ImportDescriptor(line));
                lastImportIndex = index;
            } else if (line.startsWith("public")) {
                break;
            }
        }

        javaModelDescriptor.setImportList(importDescriptorList);

        return lastImportIndex + 1;
    }

    private void parsePackageName(JavaModelDescriptor javaModelDescriptor) {
        for (String line : sourceCodeList) {
            if (line.trim().startsWith("package")) {
                javaModelDescriptor.setPackageName(line.replaceAll("(?i)[\\s]*package[\\s]+(.*);[\\s]*$", "$1").trim());
                break;
            }
        }
    }

}
